#ifndef __EPSILON_TESTBED__SOUND_H__
#define __EPSILON_TESTBED__SOUND_H__

// Standard
//#include <AL/al.h>

// OS X
#ifdef __APPLE__
# include <OpenAL/al.h>
# include <OpenAL/alc.h>
#else
# include <al.h>
# include <alc.h>
# pragma comment (lib, "OpenAL32.lib")
#endif

#include <cstring> // NULL


/* Debug Support */
ALenum
eTB_alGetError (const char* trace);


class OpenALSource;
class OpenALBuffer;

class OpenALContext
{
public:
  OpenALContext (void) {
    device_ = NULL;
  }

  ~OpenALContext (void) {
    shutdown ();
  }

  void init (void)
  {
    /* Open Default Device. */
    device_ = alcOpenDevice (NULL);

    if (device_ != NULL) {
      context_ = alcCreateContext (device_, NULL);

      if (context_ != NULL) {
        alcMakeContextCurrent (context_);
      }
    }
  }

  bool shutdown (void)
  {
    bool ret = false;

    if (device_ != NULL) {
      if (context_ != NULL) {
        alcMakeContextCurrent (NULL);
        alcDestroyContext     (context_);

        if (eTB_alGetError (__FUNCTION__) != AL_NO_ERROR)
          return false;

        context_ = NULL;
      }

      ret     = alcCloseDevice (device_);
      device_ = NULL;
    }

    return ret;
  }

  OpenALSource* createSource (void);
  OpenALBuffer* createBuffer (void);

  bool destroySource (OpenALSource* source);
  bool destroyBuffer (OpenALBuffer* buffer);

protected:
private:
  /* TODO: Keep a list of owned resources. */

  ALCdevice*  device_;
  ALCcontext* context_;
} extern *sound_context;

class OpenALListener
{
public:
protected:
private:
};

/* TODO OpenALBuffer and OpenALSource both share many characteristics... */
class OpenALBuffer
{
friend class OpenALContext;
friend class OpenALSource;
public:
  OpenALBuffer (OpenALContext* parent, ALuint handle) : context_ (parent),
                                                        handle_   (handle)
  {

  }

  /* TODO: Error Handling. */
  void
  loadData (void* data, ALsizei size, ALsizei frequency = 11025, ALenum format = AL_FORMAT_MONO8)
  {
    format_    = format;
    data_      = data;
    size_      = size;
    frequency_ = frequency;

    alBufferData (handle_, format_, data_, size_, frequency_);

    // Trace Error Execution, even if we do not do anythinga bout errors...
    eTB_alGetError (__FUNCTION__);
  }

protected:
private:
  ALenum  format_;
  ALvoid* data_;
  ALsizei size_;
  ALsizei frequency_;

  ALuint  handle_;

  OpenALContext* context_;
};

class OpenALSource
{
friend class OpenALContext;
public:
  OpenALSource (OpenALContext* parent, ALuint handle) : context_ (parent),
                                                        handle_  (handle)
  {
    attached_ = NULL;
  }

  bool play   (void)
  {
    if (attached_ != NULL) {
      alSourcePlay (handle_);

      return true;
    }

    return false;
  }

  bool attach (OpenALBuffer* buffer)
  {
    if (attached_ == NULL) {
      alSourcei (handle_, AL_BUFFER, buffer->handle_);
      alSourcei (handle_, AL_LOOPING, AL_FALSE);

      attached_ = buffer;

      return true;
    }

    return false;
  }

  bool detach (OpenALBuffer* buffer)
  {
    if (attached_ == buffer) {
      alSourcei (handle_, AL_BUFFER, NULL);

      attached_ = NULL;

      return true;
    }

    return false;
  }

protected:
private:
  OpenALBuffer*  attached_;
  ALuint         handle_;
  OpenALContext* context_;
};

#endif /* __EPSILON_TESTBED__SOUND_H__ */
